//
// Created by 49647 on 2022/4/10.
//

#ifndef AHANDLER_ALOOPER_H
#define AHANDLER_ALOOPER_H

#include <cstdint>
#include <string>
#include <list>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <unordered_map>

struct AHandler;
struct AMessage;
struct AReplyToken;

struct ALooper : public std::enable_shared_from_this<ALooper> {
    static std::shared_ptr<ALooper> Create();
    void setName(const std::string &name);
    void registerHandler(const std::shared_ptr<AHandler> &handler);
    void unregisterHandler();
    int start();
    void stop();
    static int64_t GetNowUs();

protected:
    ALooper() = default;
    virtual ~ALooper();
    static void Destroy(ALooper *);

private:
    bool loop();

    // posts a message on this looper with the given timeout
    void post(const std::shared_ptr<AMessage> &msg, int64_t delayUs);

    // creates a reply token to be used with this looper
    std::shared_ptr<AReplyToken> createReplyToken();

    // waits for a response for the reply token.  If status is OK, the response
    // is stored into the supplied variable.  Otherwise, it is unchanged.
    int awaitResponse(const std::shared_ptr<AReplyToken> &replyToken, std::shared_ptr<AMessage> *response);

    // posts a reply for a reply token.  If the reply could be successfully posted,
    // it returns OK. Otherwise, it returns an error value.
    int postReply(const std::shared_ptr<AReplyToken> &replyToken, const std::shared_ptr<AMessage> &msg);

private:
    friend struct AMessage;
    std::string mName;
    std::weak_ptr<AHandler> mHandler;

    struct Event {
        int64_t mWhenUs;
        std::shared_ptr<AMessage> mMessage;
    };
    std::list<Event> mEventQueue;
    std::mutex mLock;
    std::condition_variable mQueueChangedCondition;
    std::unique_ptr<std::thread> mThread = nullptr;
    bool mExit = false;

    // use a separate lock for reply handling, as it is always on another thread
    // use a central lock, however, to avoid creating a mutex for each reply
    std::mutex mRepliesLock;
    std::condition_variable mRepliesCondition;
};


#endif //AHANDLER_ALOOPER_H
